package com.apex.shared.core.utils;

import lombok.experimental.UtilityClass;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 随机数工具类，提供安全和非安全的随机数生成方法
 *
 * @author MoNaiHui
 */
@UtilityClass
public class RandomUtils{

    private static final String NUMBERS = "0123456789";
    private static final String LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String SPECIAL_CHARS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();

    // ---------------------- 基础随机数生成 ----------------------

    /**
     * 生成指定范围内的随机整数 [min, max]
     */
    public static int randomInt(int min, int max) {
        return ThreadLocalRandom.current().nextInt(min, max + 1);
    }

    /**
     * 生成指定范围内的随机长整数 [min, max]
     */
    public static long randomLong(long min, long max) {
        return ThreadLocalRandom.current().nextLong(min, max + 1);
    }

    /**
     * 生成指定范围内的随机双精度浮点数 [min, max)
     */
    public static double randomDouble(double min, double max) {
        return ThreadLocalRandom.current().nextDouble(min, max);
    }

    /**
     * 生成随机布尔值
     */
    public static boolean randomBoolean() {
        return ThreadLocalRandom.current().nextBoolean();
    }

    // ---------------------- 字符串生成 ----------------------

    /**
     * 生成指定长度的随机数字字符串
     */
    public static String randomNumbers(int length) {
        return random(length, NUMBERS);
    }

    /**
     * 生成指定长度的随机字母字符串
     */
    public static String randomLetters(int length) {
        return random(length, LETTERS);
    }

    /**
     * 生成指定长度的随机字母和数字组合的字符串
     */
    public static String randomAlphanumeric(int length) {
        return random(length, LETTERS + NUMBERS);
    }

    /**
     * 生成指定长度的随机密码（包含字母、数字和特殊字符）
     */
    public static String randomPassword(int length) {
        if (length < 3) {
            throw new IllegalArgumentException("Password length must be at least 3");
        }

        // 确保密码包含至少一个数字、一个字母和一个特殊字符
        StringBuilder password = new StringBuilder(length);
        password.append(random(1, NUMBERS));
        password.append(random(1, LETTERS));
        password.append(random(1, SPECIAL_CHARS));

        // 生成剩余的字符
        String allChars = NUMBERS + LETTERS + SPECIAL_CHARS;
        password.append(random(length - 3, allChars));

        // 打乱字符顺序
        List<Character> chars = password.chars()
                .mapToObj(ch -> (char) ch)
                .collect(Collectors.toList());
        Collections.shuffle(chars, SECURE_RANDOM);

        return chars.stream()
                .map(String::valueOf)
                .collect(Collectors.joining());
    }

    // ---------------------- 安全随机数生成 ----------------------

    /**
     * 生成安全的随机字节数组
     */
    public static byte[] secureRandomBytes(int length) {
        byte[] bytes = new byte[length];
        SECURE_RANDOM.nextBytes(bytes);
        return bytes;
    }

    /**
     * 生成安全的随机整数
     */
    public static int secureRandomInt() {
        return SECURE_RANDOM.nextInt();
    }

    /**
     * 生成安全的指定范围随机整数 [min, max]
     */
    public static int secureRandomInt(int min, int max) {
        return min + SECURE_RANDOM.nextInt(max - min + 1);
    }

    // ---------------------- 集合操作 ----------------------

    /**
     * 从列表中随机获取一个元素
     */
    public static <T> T randomElement(List<T> list) {
        if (list == null || list.isEmpty()) {
            return null;
        }
        return list.get(ThreadLocalRandom.current().nextInt(list.size()));
    }

    /**
     * 从列表中随机获取指定数量的元素
     */
    public static <T> List<T> randomElements(List<T> list, int count) {
        if (list == null || list.isEmpty() || count <= 0) {
            return new ArrayList<>();
        }
        count = Math.min(count, list.size());
        List<T> copy = new ArrayList<>(list);
        Collections.shuffle(copy, ThreadLocalRandom.current());
        return copy.subList(0, count);
    }

    /**
     * 生成不重复的随机整数列表
     */
    public static List<Integer> uniqueRandomInts(int min, int max, int count) {
        if (count > (max - min + 1)) {
            throw new IllegalArgumentException("Cannot generate more unique numbers than the range allows");
        }
        return IntStream.rangeClosed(min, max)
                .boxed()
                .collect(Collectors.collectingAndThen(
                        Collectors.toList(),
                        list -> {
                            Collections.shuffle(list, ThreadLocalRandom.current());
                            return list.subList(0, count);
                        }
                ));
    }

    // ---------------------- 工具方法 ----------------------

    /**
     * 生成指定长度的随机字符串
     */
    private static String random(int length, String chars) {
        if (length < 0) {
            throw new IllegalArgumentException("Length must be positive");
        }
        if (chars == null || chars.isEmpty()) {
            throw new IllegalArgumentException("Chars string cannot be empty");
        }
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            int index = SECURE_RANDOM.nextInt(chars.length());
            sb.append(chars.charAt(index));
        }
        return sb.toString();
    }

    /**
     * 生成UUID（不含横线）
     */
    public static String uuid() {
        return java.util.UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成带权重的随机索引
     *
     * @param weights 权重数组
     * @return 随机索引
     */
    public static int randomIndexByWeight(int[] weights) {
        if (weights == null || weights.length == 0) {
            throw new IllegalArgumentException("Weights array cannot be empty");
        }
        int totalWeight = IntStream.of(weights).sum();
        int randomWeight = ThreadLocalRandom.current().nextInt(totalWeight);
        int currentWeight = 0;
        for (int i = 0; i < weights.length; i++) {
            currentWeight += weights[i];
            if (randomWeight < currentWeight) {
                return i;
            }
        }
        return weights.length - 1;
    }
} 